[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Additions.IdentityConstraint.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Additions.IdentityConstraint.debug.js", "application/x-javascript")]

namespace BM.Tools.WebControls.MForm.Controls.Additions
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Web.UI;
    using System.Web.UI.WebControls;

    using AjaxControlToolkit;

    using BM.Tools.WebControls.MForm.Controls.Items;
    using BM.Tools.WebControls.MForm.Controls.ValueHandlers;
    using BM.Tools.WebControls.MForm.Navigation;

    using Helper;

    using IdentityConstraints;

    /// <summary>
    /// A constraint that makes some of the items keys and other - key references
    /// Keys must be unique, while key references must be within the values of keys
    /// </summary>
    [ClientScriptResource("BM.Tools.WebControls.MForm",
            "BM.Tools.WebControls.MForm.Controls.Additions.IdentityConstraint.js")]
    [ParseChildren(true, "References")]
    [PersistChildren(false)]
    public class IdentityConstraint : Constraint
    {
        private bool autoFill = true;

        private IEnumerable<LeafItem> keyItems;

        private OffspringsNavigator keyNavigator;

        private List<OffspringsNavigator> keyrefNavigators;

        private string name = "default";

        private Collection<KeyRef> references = new Collection<KeyRef>();

        /// <summary>
        /// Gets or sets a value indicating whether keys should autoFill.
        /// </summary>
        public bool AutoFill
        {
            get
            {
                return autoFill;
            }

            set
            {
                autoFill = value;
            }
        }

        /// <summary>
        /// Gets or sets a name of the method that is invoked for autoFill. 
        /// </summary>
        public string AutoFillMethodName
        {
            get;
            set;            
        }

        /// <summary>
        /// Gets or sets the name of the identity constraint.
        /// </summary>
        [AttributeProperty(Name = "name")]
        [DefaultValue("default")]
        public string Name
        {
            get
            {
                return name;
            }

            set
            {
                name = value;
            }
        }

        /// <summary>
        /// Gets References.
        /// </summary>
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public Collection<KeyRef> References
        {
            get
            {
                return references;
            }
        }

        /// <summary>
        /// Gets or sets Selector.
        /// </summary>
        public string Selector
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the key items.
        /// </summary>
        protected IEnumerable<LeafItem> KeyItems
        {
            get
            {
                if (keyItems == null)
                {
                    keyItems = KeyNavigator.Select().Leafs();
                }

                return keyItems;
            }
        }

        /// <summary>
        /// Gets the key navigator.
        /// </summary>
        protected OffspringsNavigator KeyNavigator
        {
            get
            {
                if (keyNavigator == null)
                {
                    keyNavigator = new OffspringsNavigator(Owner, Selector);
                }

                return keyNavigator;
            }
        }

        /// <summary>
        /// Gets the keyref navigators.
        /// </summary>
        protected IEnumerable<OffspringsNavigator> KeyrefNavigators
        {
            get
            {
                if (keyrefNavigators == null)
                {
                    keyrefNavigators = new List<OffspringsNavigator>();
                    foreach (KeyRef kr in References)
                    {
                        keyrefNavigators.Add(new OffspringsNavigator(Owner, kr.Selector));
                    }
                }

                return keyrefNavigators;
            }
        }

        /// <summary>
        /// Clones the identity constraint
        /// </summary>
        /// <returns>
        /// the cloned identity constraint
        /// </returns>
        public Addition Clone()
        {
            var clone = new IdentityConstraint
                        {
                                AutoFill = AutoFill, 
                                AutoFillMethodName = AutoFillMethodName,
                                Name = Name,                                
                                Selector = Selector
                        };
            foreach (KeyRef keyRef in References)
            {
                clone.References.Add(keyRef.Clone());
            }

            return clone;
        }

        /// <summary>
        /// Checks if keys are unique and key references are of key values
        /// </summary>
        /// <param name="item">
        /// The item that is the root of the identity constraint
        /// </param>
        /// <returns>
        /// is the constraint met
        /// </returns>
        public override bool IsValid(OffspringItem item)
        {
            var keyValues = GetKeysValues();
            if (keyValues == null)
            {
                return false;
            }

            return HaveKeyRefsKeyValues(keyValues);
        }

        /// <summary>
        /// Adds Key and KeyRef controls to selected child items
        /// </summary>
        /// <param name="e">
        /// The event args.
        /// </param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            foreach (OffspringItem item in KeyItems)
            {
                item.Additions.Add(new Key(Name, KeyNavigator.GetReversePath(), AutoFill, AutoFillMethodName));
            }

            foreach (OffspringsNavigator keyrefNavigator in KeyrefNavigators)
            {
                PrepareKeyRefs(keyrefNavigator);
            }
        }
        
        private static IListControlValueHandler CreateOrGetListControlValueHandler(LeafItem item)
        {
            return item.ValueHandler as IListControlValueHandler
                          ?? new DropDownListValueHandler { EnableViewState = false };            
        }
        
        private HashSet<string> GetKeysValues()
        {
            var values = new HashSet<string>();
            foreach (var keyItem in KeyItems)
            {
                if (!values.Contains(keyItem.Value))
                {
                    values.Add(keyItem.Value);
                }
                else
                {
                    return null;
                }
            }

            return values;
        }
        
        private bool HaveKeyRefsKeyValues(ICollection<string> keyValues)
        {
            var keyrefItems = KeyrefNavigators.SelectMany(keyRef => keyRef.Select()).Leafs();
            foreach (var keyrefItem in keyrefItems)
            {
                if (!keyValues.Contains(keyrefItem.Value))
                {
                    return false;
                }
            }

            return true;
        }
       
        private void PrepareKeyRefs(OffspringsNavigator keyrefNavigator)
        {
            foreach (OffspringItem item in keyrefNavigator.Select())
            {                
                PrepareKeyRefValueHandler(item as LeafItem);
                item.Additions.Add(new KeyReference(Name, keyrefNavigator.GetReversePath()));
            }
        }
        
        private void PrepareKeyRefValueHandler(LeafItem item)
        {
            IListControlValueHandler handler = CreateOrGetListControlValueHandler(item);
            foreach (LeafItem keyItem in KeyItems)
            {
                if (keyItem.IsPrototypeDescendant())
                {
                    continue;
                }

                handler.ListControl.Items.Add(new ListItem(keyItem.Value, keyItem.Value));
            }

            if (item.ValueHandler != handler)
            {
                item.ValueHandler = handler as ValueHandler;
            }            
        }
    }
}